package DBManager;


use FileHandle;
use Moose;
use DBI;

use DBAccess;
use Log;
use Utils;

has 'root' => (
    is => 'ro',
    isa => 'Str',
    );

has 'file' => (
    is => 'ro',
    isa => 'Str',
    );

has 'statements' => (
    is => 'rw' ,
    isa => 'ArrayRef[Str]',
    init_arg => undef,
    );

sub BUILD {
    my $s = shift;
    if(! -d $s->root ) {
	my $l=Log->instance;
	$l->xsupererror("d8665f89-d824-355a-b638-3120349bd9b8",
			$l->format($s->root,'',"is not a directory"));		
    }else{
	my $p=$s->getDbPath();
	if ( ! -f $p ) {
	    # a file with a zero-size is a valid sqlite file with empty content; 
	    # try to create it to check file path and rights:
	    my $fh = FileHandle->new(">$p");
	    if (defined $fh) {
		$fh->close;
	    }else{
		my $l=Log->instance;
		$l->xsupererror("6e3addc8-cbd6-3b09-98ff-600fa89d1e4b",
				$l->format($p,'',"file cannot be created"));		
	    }
	}
    }
}

sub getDbPath {
    my $s = shift;
    return $s->root ."/" . $s->file;
}

sub init {
    my $s = shift;
    my $a = DBAccess->instance();    
    $a->filename($s->getDbPath());
    if($a->opened) {
	$a->close();
    }
    $a->remove();
    $a->open();
    $s->_create_db($a);
    $s->statements( [ ] );
}

sub _create_db {
    my $s = shift;
    my $a = shift;
    $a->do("CREATE TABLE subject   (subj TEXT , k TEXT, v TEXT, PRIMARY KEY(subj,k) )"); 
    $a->do("CREATE TABLE assertion (subj TEXT , property TEXT, obj TEXT)");
    $a->do("CREATE TABLE invalid   (subj TEXT PRIMARY KEY, timestamp TEXT)");
    $a->do("CREATE INDEX idx_subject ON subject   (subj ASC,k ASC       ,v)");
    $a->do("CREATE INDEX idx_assertion ON assertion (subj ASC,property ASC,obj ASC)");
}

sub invalidation {
    if(@_ != 2) {
	my $l=Log->instance;
	$l->xinternal("4408da5b-22d4-336a-9706-e4ca0413f744",
		      $l->format((caller(0))[3],'',"bad number of parameters"));			
    }
    my $s = shift;
    my $now = Utils::now();
    push(@{$s->statements},"INSERT OR REPLACE INTO invalid (subj,timestamp) values ('$_[0]','$now');");
}

sub invalidated {
    if(@_ != 2) {
	my $l=Log->instance;
	$l->xinternal("beaaa88b-59c9-333d-98a5-565bb6dd7aad",
		      $l->format((caller(0))[3],'',"bad number of parameters"));			
    }
    my $s = shift;
    my $a = DBAccess->instance();    
    my $select = $a->exec_query("SELECT timestamp FROM invalid WHERE subj ='$_[0]'",
				'timestamp');
    my $n = @{$select};
    if($n == 1) {
	return $select->[0]->{'timestamp'};
    }elsif($n == 0) {
	return undef;
    }else{
	my $l=Log->instance;
	$l->xinternal("a8f15641-2ef2-3a7a-bebe-959b5bc6399c",
		      $l->format((caller(0))[3],'',"bad number of result in SELECT statement"));		     
    }    
}

sub addSubject {
    my $s = shift;
    if(@_ != 3) {
	my $l=Log->instance;
	$l->xinternal("642d61de-e763-3664-805f-aea013543119",
		      $l->format((caller(0))[3],'',"bad number of parameters"));			
    }
    push(@{$s->statements},"INSERT OR REPLACE INTO subject (subj,k,v) values ('$_[0]','$_[1]','$_[2]');");
}

sub assert {
    my $s = shift;
    if((@_ == 3)) {
	push(@{$s->statements},"INSERT OR REPLACE INTO assertion (subj,property,obj) values ('$_[0]','$_[1]','$_[2]');");
    }elsif(@_  == 2) {
	push(@{$s->statements},"INSERT OR REPLACE INTO assertion (subj,property) values ('$_[0]','$_[1]');");
    }else{
	my $l=Log->instance;
	$l->xinternal("42778da6-c4c2-3ee9-8450-92c00717f325",
		      $l->format((caller(0))[3],'',"bad number of parameters"));			
    }
}



sub getSubject {
    my $s = shift;
    my $a = DBAccess->instance();
    my $select = $a->exec_query("SELECT subj,k,v FROM subject WHERE subj in ('".
			     join("','",@_)."')",
			     'subj','k','v');
    my $res = { };
    foreach my $rec (@{$select}) {
	if(!defined($res->{$rec->{'subj'}})) { 
	    $res->{$rec->{'subj'}} = { };	    
	};
	if(defined($res->{$rec->{'subj'}}->{$rec->{'k'}})) { 
	   # internal
	};
	$res->{$rec->{'subj'}}->{$rec->{'k'}} = $rec->{'v'};
    }
    return $res;
}

sub getAssertions {
    my $s = shift;
    my $a = DBAccess->instance();
    my $select = $a->exec_query("SELECT subj,property,obj FROM assertion WHERE subj in ('".
			     join("','",@_)."')",
			     'subj','property','obj');
    my $res = { };
    foreach my $rec (@{$select}) {
	if(!defined($res->{$rec->{'subj'}})) { 
	    $res->{$rec->{'subj'}} = { };	    
	};
	if(!defined($res->{$rec->{'subj'}}->{$rec->{'property'}})) { 
	    if($rec->{'obj'}) {
		$res->{$rec->{'subj'}}->{$rec->{'property'}} = { };
	    }else{
		$res->{$rec->{'subj'}}->{$rec->{'property'}} = 1;
	    }
	};
	if($rec->{'obj'}) {
	    # if obj is not NULL (a property valued assertion)
	    if($res->{$rec->{'subj'}}->{$rec->{'property'}} == 1) {
		my $l=Log->instance;
		$l->xinternal("8c0e518d-d7ed-31e8-8cda-6aeafa004f10",
			      $l->format($rec->{'subj'},$rec->{'property'},"attempt to write an ojbect  on a property-only subject-property. object=".$rec->{'obj'}));			    
	    }else{
		$res->{$rec->{'subj'}}->{$rec->{'property'}}->{$rec->{'obj'}} = 1;
	    }
	}else{
	    # if obj is NULL (a property only assertion)
	    if($res->{$rec->{'subj'}}->{$rec->{'property'}} != 1) {
		my $l=Log->instance;
		$l->xinternal("333c593d-0952-3c27-9995-6486b6a9334b",		   
			      $l->format($rec->{'subj'},$rec->{'property'},"attempt to write a property-only subject with object on a property-valued subject. object=".$rec->{'obj'}));			    	
	    }
	}

    }
    return $res;
}

sub disassert {
    my $s = shift;
    if(@_ == 1) {
	push(@{$s->statements},"DELETE FROM assertion WHERE (subj = '$_[0]');");	
    }elsif(@_ == 2) {
	push(@{$s->statements},"DELETE FROM assertion WHERE (subj = '$_[0]' and property = '$_[1]');");	
    }elsif(@_ == 3){
	push(@{$s->statements},"DELETE FROM assertion WHERE (subj = '$_[0]' and property = '$_[1]' and obj = '$_[2]');");	
    }else{
	my $l=Log->instance;
	$l->xinternal("1741f634-13b6-3616-adee-6b2675460e4c",
		      $l->format((caller(0))[3],'',"bad number of parameters ".scalar(@_)));			       
    }
}

sub delete {
     my $s = shift;
     if(@_ == 1) {
	 $s->disassert($_[0]);
	 push(@{$s->statements},"DELETE FROM subject WHERE (subj = '$_[0]');");
	 push(@{$s->statements},"DELETE FROM invalid WHERE (subj = '$_[0]');");
     }else{
	my $l=Log->instance;
	$l->xinternal("5189a679-1d02-3538-a31c-d93ef8b7d9fb",
		      $l->format((caller(0))[3],'',"bad number of parameters ".scalar(@_)));			     
     }
}

sub execute {
    my $s = shift;    
    if(@{$s->statements} > 0) {
	my $a = DBAccess->instance();
	my $l=Log->instance;
	$l->xdump(3,'execute statemets',join("\n",@{$s->statements}));
	$a->do("BEGIN");
	$a->do($_) foreach (@{$s->statements});
	$a->do("COMMIT");
#       my $sql = "BEGIN;\n" . join("\n",@{$s->statements}) ."\nCOMMIT;\n";
#	$a->do($sql);
	$s->statements( [ ] );

    }
}


no Moose;
__PACKAGE__->meta->make_immutable;

